iT邦幫忙

2022 iThome 鐵人賽

DAY 24
2
Modern Web

強化 JavaScript 之 - 程式語感是可以磨練成就的系列 第 24

Day24-JavaScript 的 WeakSet & WeakMap 資料結構

  • 分享至 

  • xImage
  •  

前言

昨天瞭解了兩種資料結構: Set & Map 後,其實還有另外兩個和它們相似的資料結構,也就是 WeakSet & WeakMap,來一起瞭解它們吧!

不過在那之前,要先來介紹一個和 WeakSet & WeakMap 很有關係的名詞 - 弱引用。


弱引用(weakly reference)介紹

複習 GC 觀念

用簡化後的話來說,當一個物件沒有任何 reference 時,會被定期自動 GC 掉,例如以下圖片中,右側的三個物件因為都和 roots 物件,沒有直接或是間接的 reference,所以會被 GC。

這個是我們在 Day3 認識到的內容,若關於 GC 機制等觀念印象模糊的話可以回去看 Day3!

WeakSet & WeakMap 的弱引用

而這篇文章介紹的 WeakSet & WeakMap 它們比較特別,都是屬於弱引用,也就是說,若 WeakSet & WeakMap 引用了某個物件型別的值當作 key 值使用,若被引用的那個物件被設定成 null 並且沒有其他引用存在時,就會在未來某一時刻被自動 GC。

後面 WeakMap 介紹的第二點有範例說明,閱讀後會更加明確些。

而特別的是 Set & Map 對於物件的引用是強引用,強引用的話即使被當作 key 值的物件被修改成了 null,但因為 Map 還可以用它的一些方法例如 Map.prototype.keys() 做遍歷,所以還是有些方式可以取到該物件,當然不會自動 GC。

let ray = { name: "John" };

const map = new Map();
map.set(ray, "...");

ray = null; // 不會被回收,可透過 Map.keys() 取用

WeakRef

除了 WeakSet & WeakMap 使用了弱引用,還有其他地方有用到弱引用嗎?答案是有,JavaScript 有個內建的物件 WeakRef,就可以用來建立弱引用的物件,有興趣的讀者可以參考 MDN 文件:

MDN WeakRef


WeakMap 介紹

WeakMap 和 Map 非常相似,兩者差異在於以下幾點:

1. WeakMap 的 key 必須是物件型別。

Map 資料結構的 key可以是各種資料型態的值(字串/數字/物件…等),但 WeakMap 的 key 必須是物件型別。

2. WeakMap 的 key 為弱引用

這裡舉個範例,建立 WeakMap 和 Map 物件各一個,然後我們將它們的 key 值設定為 null,雖然當下兩個物件都還能保持它們的 key value,但過一段不定期的時間後觸發 GC 後,WeakMap 的 key 就會被回收,像截圖是隔十分鐘後再重新印出會發現 WeakMap 已被清空。

範例程式碼:

let keyObj = {};
const exampleWM = new WeakMap();
exampleWM.set(keyObj, 'example');
keyObj = null;
console.log(exampleWM);

let keyObj2 = {};
const exampleMap = new Map();
exampleMap.set(keyObj2, 'example');
keyObj2 = null;
console.log(exampleMap);

3. WeakMap 無法遍歷 key

WeakMap 不像 Map 有 keys()forEach()entries()...等方法,實際上 WeakMap 也只有四個方法可以使用而已,可參考: MDN WeakMap Instance methods

WeakMap 應用

以個人經驗來說,我自己是還沒在實務開發用到 WeakMap,它確實不常被使用到,但它可以有效的做垃圾回收,所以只需要短暫在瀏覽器存放額外資料的情境就適合使用它,例如過一段時間就清除的快取之類的。

這邊分享一位日本高手工程師 Daishi Kato 和他的 Github,他貢獻了多個和 React 相關的開源專案,其中一個專案 proxy-memoize 就有用到 WeakMap 去做快取機制,從 proxy-memoize 的原始碼 可以看到多處都有使用到 WeakMap,以下節錄有使用到的地方:

type Affected = WeakMap<object, Set<string | number | symbol>>; // line 8,搭配 WeakMap 定義一個 Type alias(型別別名)

const resultCache = new WeakMap<Obj, Entry>(); // line 70,存放 cache 的地方
const proxyCache = new WeakMap(); // line 70

// line 82 也有用到

const affected: Affected = new WeakMap(); // line 88,使用 line 8 定義的 Type alias 並給予初始值 WeakMap

WeakSet 介紹

如同 WeakMap 和 Map 的關係,WeakSet 和 Set 也非常相似,兩者差異在於以下幾點:

1. WeakSet 中的值必須是物件型別

2. WeakSet 內部的值為弱引用

若其他地方沒有引用到 WeakSet 內部的值,會不定期被 GC。

3. WeakSet 也無法遍歷

WeakSet 它只有 add()、has()、delete() 等方法,並沒有 Set 的 size 和 keys() 方法。

WeakSet 應用

我自己也還沒在實務開發用到 WeakSet,搜尋了網路上在實際開發上的應用也沒有找到,但還是一樣的原則,如果只是需要短暫暫存資料,未來不需要就移除的話,就可以使用它。


這篇就到這邊啦~若讀者們知道更多關於 WeakMap & WeakSet 的實際應用範例的話,非常歡迎留言補充,我也想知道~

參考資料 & 推薦閱讀

JS WeakMap应该什么时候使用

介紹 WeakMap

WeakMap and WeakSet


上一篇
Day23-JavaScript 的 Set/Map 資料結構
下一篇
Day25-認識與實作 Debounce 和 Throttle
系列文
強化 JavaScript 之 - 程式語感是可以磨練成就的30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

1
json_liang
iT邦研究生 5 級 ‧ 2022-09-24 15:17:11

這個資料結構真的很重要!

1
雷N
iT邦研究生 1 級 ‧ 2022-09-24 21:01:13

真的第一次聽到的weakmap , weakset
感謝大大分享

我要留言

立即登入留言